/*
 *                   江城子 . 程序员之歌
 * 
 *               十年生死两茫茫，写程序，到天亮。
 *                   千行代码，Bug何处藏。
 *               纵使上线又怎样，朝令改，夕断肠。
 * 
 *               领导每天新想法，天天改，日日忙。
 *                   相顾无言，惟有泪千行。
 *               每晚灯火阑珊处，夜难寐，加班狂。
 * 
 */

/*
 * @Author: 火红色祥云 809880192@qq.com
 * @Date: 2022-06-28 20:24:19
 * @LastEditors: 火红色祥云 809880192@qq.com
 * @LastEditTime: 2022-10-06 21:27:07
 * @FilePath: /imx6ull-app/17_input/17_inputkey.c
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include "linux/kernel.h"
#include "linux/ide.h"
#include "linux/init.h"
#include "linux/module.h"

#include "linux/input.h"
#include "linux/of.h"
#include "linux/of_gpio.h"
#include "linux/timer.h"
#include "linux/irq.h"
#include "linux/types.h"
#include "asm/mach/map.h"
#include "asm/uaccess.h"
#include "asm/io.h"
#include "linux/of_irq.h"

struct irq_keydes{
    unsigned int gpio;
    int irqnum;
    unsigned char value;
    char name[10];
    irqreturn_t (*handler)(int,void *);     //中断服务函数
};

struct keyinput_dev{
    struct device_node  *st_node;
    struct timer_list   time;
    struct irq_keydes   st_key0;
    struct input_dev    *st_inputdev;
};

struct keyinput_dev keyinputdev;

//按键中断服务函数
irqreturn_t key0_handler(int irq,void *dev_id)
{
    struct keyinput_dev *dev = (struct keyinput_dev *)dev_id;
    printk("key0_handler\r\n");
    dev->time.data = (volatile long)dev_id;
    mod_timer(&dev->time,jiffies+msecs_to_jiffies(10));
    return IRQ_RETVAL(IRQ_HANDLED);
}

//定时器中断函数
void timer_fun(unsigned long arg)
{
    #if 0
    unsigned char keyvalue;
    struct keyinput_dev *dev = (struct keyinput_dev *)arg;

    keyvalue = gpio_get_value(dev->st_key0.gpio);
    if (keyvalue == 0)                              //按键按下,按键中断时按键已被按下或者抬起，到这里已经10ms,消抖
    {
        input_report_key(dev->st_inputdev,dev->st_key0.value,0);
        input_sync(dev->st_inputdev);
        printk("report 0\r\n");
    }
    else
    {
        input_report_key(dev->st_inputdev,dev->st_key0.value,1);
        input_sync(dev->st_inputdev);
        printk("report 1\r\n");
    }
    #else

    unsigned char value;

	struct irq_keydes *keydesc;
	struct keyinput_dev *dev = (struct keyinput_dev *)arg;

	keydesc = &dev->st_key0;
    
	value = gpio_get_value(keydesc->gpio); 	/* 读取IO值 */
	if(value == 0){ 						/* 按下按键 */
		/* 上报按键值 */
		//input_event(dev->inputdev, EV_KEY, keydesc->value, 1);
		input_report_key(dev->st_inputdev, keydesc->value, 1);/* 最后一个参数表示按下还是松开，1为按下，0为松开 */
		input_sync(dev->st_inputdev);
	} else { 									/* 按键松开 */
		//input_event(dev->inputdev, EV_KEY, keydesc->value, 0);
		input_report_key(dev->st_inputdev, keydesc->value, 0);
		input_sync(dev->st_inputdev);
	}

    #endif
}

static int keyio_init(void)
{
    int retval = 0;

    //找到key设备树节点
    keyinputdev.st_node = of_find_node_by_path("/lux_key");
    if (keyinputdev.st_node == NULL)
    {
        printk("lux_key node not found\r\n");
        return -1;
    }
    //找到key的gpio
    keyinputdev.st_key0.gpio = of_get_named_gpio(keyinputdev.st_node,"key-gpio",0);
    if (keyinputdev.st_key0.gpio < 0)
    {
        printk("key gpio not found\r\n");
        return -1;
    }
    //申请key的gpio
    gpio_request(keyinputdev.st_key0.gpio,"key0gpio_lable");//static inline int gpio_request(unsigned gpio, const char *label)
    //设置为输入
    gpio_direction_input(keyinputdev.st_key0.gpio);
    //查找中断号
    keyinputdev.st_key0.irqnum = irq_of_parse_and_map(keyinputdev.st_node,0);//unsigned int irq_of_parse_and_map(struct device_node *dev,int index)
    //设置中断服务函数
    keyinputdev.st_key0.handler = key0_handler;
    //传入中断服务函数的参数
    keyinputdev.st_key0.value = KEY_0;
    //申请中断
    retval = request_irq(keyinputdev.st_key0.irqnum,
                keyinputdev.st_key0.handler,
                IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
                "key0gpio",
                &keyinputdev);//request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
    if (retval < 0)
    {
        printk("request_irq failed\r\n");
        return -1;
    }
    
    //初始化定时器
    init_timer(&keyinputdev.time);
    //设置定时器中断服务函数
    keyinputdev.time.function = timer_fun;
    //申请input设备
    keyinputdev.st_inputdev = input_allocate_device();//struct input_dev __must_check *input_allocate_device(void);
    //初始化input设备
    keyinputdev.st_inputdev->name = "keyinput";
    keyinputdev.st_inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
    input_set_capability(keyinputdev.st_inputdev,EV_KEY,KEY_0);
    //向内核注册input设备
    retval = input_register_device(keyinputdev.st_inputdev);
    if (retval)
    {
        printk("register input device failed\r\n");
        return retval;
    }
    return 0;
}
static int __init inputkey_init(void)
{
    keyio_init();
    
    return 0;
}

static void __exit inputkey_exit(void)
{
    del_timer_sync(&keyinputdev.time);
    free_irq(keyinputdev.st_key0.irqnum,&keyinputdev);
    //void input_unregister_device(struct input_dev *);
    input_unregister_device(keyinputdev.st_inputdev);
    //void input_free_device(struct input_dev *dev);
    input_free_device(keyinputdev.st_inputdev);
}

module_init(inputkey_init);
module_exit(inputkey_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lux");
